#version 300 es

// 设置精度
precision highp float;
precision highp int;
precision highp sampler3D;

// 输入参数
uniform vec2 uWindowSize;                    // 窗口大小
uniform bool uSampleCloudNoise;              // 是否采样云噪声
uniform float uT;                            // 时间
uniform float uTModded;                      // 修改后的时间
uniform float uRCloud0;                      // 云层底部半径
uniform float uRCloud1;                      // 云层顶部半径
uniform float uCloudBaseDensityStart;        // 云层底部密度起始值
uniform float uCloudTopDensityStart;         // 云层顶部密度起始值
uniform float uCloudTopDensityEnd;           // 云层顶部密度结束值
uniform float uR1;                           // 大气层半径
uniform float uRSun;                         // 太阳半径
uniform float uRPlanet;                      // 行星半径
uniform vec3 uLightPos;                      // 光源位置
uniform float uMarchSamples;                 // 步进采样数
uniform vec3 uCloudScattering;               // 云层散射系数
uniform float uWeatherTexScale;              // 天气纹理缩放
uniform float uWindMagnitude;                // 风强度
uniform float uCloudTexScale;                // 云纹理缩放
uniform float uErosionTexScale;              // 侵蚀纹理缩放
uniform float uErosionThreshold;             // 侵蚀阈值
uniform float uCloudDensityMul;              // 云密度乘数
uniform float uVisibleDist;                  // 可见距离
uniform float uLocalShadowMapVisDistScale;   // 局部阴影贴图可见距离缩放
uniform float uGlobalShadowMapVisDistScale;  // 全局阴影贴图可见距离缩放
uniform mat4 uLocalShadowMapViewMat;         // 局部阴影贴图视图矩阵
uniform mat4 uGlobalShadowMapViewMat;        // 全局阴影贴图视图矩阵
uniform mat4 uObserverViewMat;               // 观察者视图矩阵

// 纹理采样器
uniform sampler3D uCloudTex;                 // 云纹理
uniform sampler3D uErosionTex;               // 侵蚀纹理
uniform sampler2D uWeatherTex;               // 天气纹理

// 输入和输出变量
in vec2 vTexCoord;                           // 纹理坐标
layout (location=0) out vec4 globalShadowMapOut;  // 全局阴影贴图输出
layout (location=1) out vec4 localShadowMapOut;   // 局部阴影贴图输出

// 数学常量
const float PI = 3.14159265359;
const float PI2 = PI * 2.;
const float inv4PI = 1. / (4. * PI);
const float PI_OVER_180 = PI / 180.0;
const float COT_HALF_FOV = 1. / (tan((30.) * PI_OVER_180));
const vec3 BOX_SIZE = vec3(1., 1., 2.);
const vec3 FULL_BOX_SIZE = vec3(BOX_SIZE.xy * 2., BOX_SIZE.z);
const vec3 PLANET_ORIGIN = vec3(0.);
const float MAX_DETAILED_DIST = 1.75;
const float SCATTERING_MULTIPLIER = 100.;
const vec3 SIGMA_ABSORPTION_CONST = 0. * vec3(.067, .055, .055);

// 大气参数
const float ATM_SCATTERING_MULTIPLIER = .03624;
const float ATM_ABSORPTION_MULTIPLIER = .00199;
const vec3 ATM_SIGMA_ABSORPTION_CONST = vec3(0.3254901960784314, 0.9450980392156862, 0.043137254901960784);

/**
 * 参与介质结构体
 * 用于描述介质的光学特性
 */
struct ParticipatingMedia {
  vec3 scattering;    // 散射系数
  vec3 extinction;    // 消光系数
};

/**
 * 阴影步进数据结构体
 * 用于存储阴影计算过程中的数据
 */
struct ShadowMarchData {
  vec3 opticalDepth;  // 光学深度
  float frontDepth;   // 前向深度
  float backDepth;    // 后向深度
};

/**
 * 生成随机数
 * @param n - 随机数种子
 * @return float - 随机数
 */
float nrand(vec2 n) {
  return fract(sin(dot(n.xy, vec2(12.9898, 78.233)))* 43758.5453);
}

/**
 * 生成带时间变化的随机数
 * @param n - 随机数种子
 * @return float - 随机数
 */
float n1rand(vec2 n) {
  float t = fract(uTModded);
  float nrnd0 = nrand(n + 0.07 * t);
  return nrnd0;
}

/**
 * 计算射线与球体的交点
 * @param r0 - 射线起点
 * @param rd - 射线方向
 * @param s0 - 球心
 * @param sR - 球半径
 * @param farthest - 是否返回最远交点
 * @return float - 交点距离，-1表示无交点
 */
float raySphereIntersect(vec3 r0, vec3 rd, vec3 s0, float sR, bool farthest) {
  float a = dot(rd, rd);
  vec3 s0_r0 = r0 - s0;
  float b = 2.0 * dot(rd, s0_r0);
  float c = dot(s0_r0, s0_r0) - (sR * sR);
  float delta = b * b - 4.0*a*c;
  if (delta < 0.0 || a == 0.0) {
    return -1.0;
  }
  float sol0 = (-b - sqrt(delta)) / (2.0*a);
  float sol1 = (-b + sqrt(delta)) / (2.0*a);
  if (sol0 < 0.0 && sol1 < 0.0) {
    return -1.0;
  }
  if (sol0 < 0.0) {
    return max(0.0, sol1);
  } else if (sol1 < 0.0) {
    return max(0.0, sol0);
  }

  if (farthest) {
    return max(0.0, max(sol0, sol1));
  } else {
    return max(0.0, min(sol0, sol1));
  }
}

/**
 * 获取高度信号
 * @param samplePos - 采样位置
 * @return float - 高度信号值
 */
float getHeightSignal (vec3 samplePos) {
  float sampleAltitude = (length(samplePos) - uRCloud0) / (uRCloud1 - uRCloud0);
  float sampleAltitudeSquard = sampleAltitude * sampleAltitude;
  return mix(smoothstep(0., uCloudBaseDensityStart, sampleAltitudeSquard), 1. - smoothstep(uCloudTopDensityStart, uCloudTopDensityEnd, sampleAltitude), sampleAltitude);
}

/**
 * 获取参与介质
 * @param samplePos - 采样位置
 * @param sampleCloudNoise - 是否采样云噪声
 * @return ParticipatingMedia - 参与介质数据
 */
ParticipatingMedia getParticipatingMedia(const vec3 samplePos, const bool sampleCloudNoise) {
  vec3 sigmaScattering;
  vec3 sigmaExtinction;
  float weatherData = textureLod(uWeatherTex, (uT * uWindMagnitude * .005 + samplePos.xz) * uWeatherTexScale, 0.).x;
  float density;
  float cloudSample;
  float coverageSignal = weatherData * getHeightSignal(samplePos);
  if (coverageSignal >= uErosionThreshold) {
    if (sampleCloudNoise) {
      density = coverageSignal * textureLod(uCloudTex, samplePos * uCloudTexScale, 0.).x;
      density *= step(uErosionThreshold, density);
    } else {
      density = coverageSignal;
    }
  }
  density = clamp(density, 0., 1.);
  sigmaScattering = uCloudDensityMul * uCloudScattering * density;
  sigmaExtinction = max(vec3(.000000001), SIGMA_ABSORPTION_CONST * density + sigmaScattering);
  return ParticipatingMedia(sigmaScattering, sigmaExtinction);
}

/**
 * 步进云层
 * @param marchOrigin - 步进起点
 * @param rayDir - 射线方向
 * @param marchDist - 步进距离
 * @param steps - 步数
 * @return ShadowMarchData - 阴影步进数据
 */
ShadowMarchData marchCloudLayer (vec3 marchOrigin, vec3 rayDir, const float marchDist, const float steps) {
  vec3 samplePos;
  float cloudFrontDepth = marchDist;
  float cloudBackDepth = 0.;
  vec3 opticalDepth = vec3(0.);
  ParticipatingMedia cloudMedia;

  float stepSize = min(marchDist / steps, .5);
  float depth = .001;

  while (depth < marchDist) {
    samplePos = marchOrigin + rayDir * depth;
    depth += stepSize;
    cloudMedia = getParticipatingMedia(samplePos, uSampleCloudNoise);
    if (cloudMedia.scattering == vec3(0.)) {
      continue;
    }
    cloudFrontDepth = min(depth, cloudFrontDepth);
    cloudBackDepth = max(depth, cloudBackDepth);
    opticalDepth += cloudMedia.extinction * stepSize;
  }

  if (cloudBackDepth == 0.) {
    cloudBackDepth = raySphereIntersect(marchOrigin, rayDir, PLANET_ORIGIN, uRCloud1, true);
  }

  return ShadowMarchData(opticalDepth, cloudFrontDepth, cloudBackDepth);
}

/**
 * 步进大气层
 * @param cameraPos - 相机位置
 * @param rayDir - 射线方向
 * @return vec4 - 渲染结果
 */
vec4 marchAtmosphere (vec3 cameraPos, vec3 rayDir) {
  float RPlanetDist = 0.;
  float RCloud0Dist = 0.;
  float RCloud1Dist = 0.;
  float RCloud0DistFar = 0.;
  float RCloud1DistFar = 0.;

  float camDist = length(cameraPos);

  vec3 marchOrigin = vec3(0.);
  float marchDist = 0.;

  RPlanetDist = raySphereIntersect(cameraPos, rayDir, PLANET_ORIGIN, uRPlanet, false);
  RCloud0Dist = raySphereIntersect(cameraPos, rayDir, PLANET_ORIGIN, uRCloud0, false);
  RCloud1Dist = raySphereIntersect(cameraPos, rayDir, PLANET_ORIGIN, uRCloud1, false);

  if (RCloud1Dist == -1.) {
    return vec4(0.);
  }

  if (camDist >= uRCloud0 && camDist < uRCloud1) {
    marchOrigin = cameraPos;
    if (RCloud0Dist == -1.) {
      marchDist = RCloud1Dist;
    } else {
      marchDist = RCloud0Dist;
    }
  } else if (camDist >= uRCloud1) {
    marchOrigin = cameraPos + rayDir * RCloud1Dist;
    if (RCloud0Dist == -1.) {
      RCloud1DistFar = raySphereIntersect(cameraPos, rayDir, PLANET_ORIGIN, uRCloud1, true);
      marchDist = RCloud1DistFar - RCloud1Dist;
    } else {
      marchDist = RCloud0Dist - RCloud1Dist;
    }
  } else if (camDist >= uRPlanet && camDist < uRCloud0) {
    marchOrigin = cameraPos + rayDir * RCloud0Dist;
    if (RPlanetDist == -1.) {
      marchDist = RCloud1Dist - RCloud0Dist;
    }
  }

  ShadowMarchData cloudLayer = marchCloudLayer(marchOrigin, rayDir, marchDist, uMarchSamples);
  return vec4(cloudLayer.opticalDepth, cloudLayer.backDepth);
}

/**
 * 主函数
 * 渲染云层阴影贴图
 */
void main () {
  // 计算光源方向
  vec3 lightDir = normalize(uLightPos);
  
  // 计算投影器位置
  vec3 projectorOrigin = lightDir * uRCloud1;
  
  // 获取观察者位置
  vec3 observerPos = (uObserverViewMat[3]).xyz;
  
  // 计算相机位置和偏移
  vec3 cameraPos;
  vec3 cameraOffset;
  vec3 observerProjectedPos;
  vec3 projectedObserverDir;
  vec3 observerRelativeToProjector;
  float projectedObserverDist;
  float offset;
  
  // 计算观察者相对于投影器的位置
  observerRelativeToProjector = observerPos - projectorOrigin;
  observerProjectedPos = observerRelativeToProjector + lightDir * dot(observerRelativeToProjector, -lightDir);
  projectedObserverDist = length(observerProjectedPos);
  projectedObserverDir = normalize(observerProjectedPos);
  
  // 设置相机位置
  cameraPos = projectorOrigin + projectedObserverDir * min(projectedObserverDist, uRCloud1);

  // 计算全局阴影贴图
  offset = uVisibleDist * uGlobalShadowMapVisDistScale;
  cameraOffset = (uGlobalShadowMapViewMat * vec4((vTexCoord * 2. - 1.) * offset, 0., 0.)).xyz;
  globalShadowMapOut = marchAtmosphere(cameraPos + cameraOffset, -lightDir);

  // 计算局部阴影贴图
  offset = uVisibleDist * uLocalShadowMapVisDistScale;
  cameraOffset = (uGlobalShadowMapViewMat * vec4((vTexCoord * 2. - 1.) * offset, 0., 0.)).xyz;
  localShadowMapOut = marchAtmosphere(cameraPos + cameraOffset, -lightDir);
}
